package com.zoe.onelink.business.autoconfigure;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.zoe.onelink.auth.token.authenticate.OnelinkTokenAuthenticateCallback;
import com.zoe.onelink.business.dao.ParamDao;
import com.zoe.onelink.business.endpoint.OnelinkBizEndpoint;
import com.zoe.onelink.business.filter.WrapParameterFilter;
import com.zoe.onelink.business.i18n.I18nBizCustomMessageProvider;
import com.zoe.onelink.business.interceptor.BizHeaderInterceptor;
import com.zoe.onelink.business.mybatis.interceptor.BatchByJdbcInterceptor;
import com.zoe.onelink.business.mybatis.interceptor.MybatisAdvanceQueryInnerInterceptor;
import com.zoe.onelink.business.param.BizParamInitializer;
import com.zoe.onelink.business.property.OnelinkBizProperties;
import com.zoe.onelink.business.property.OnelinkExternalSystemProperties;
import com.zoe.onelink.business.resolver.OnelinkRequestHeaderMethodArgumentResolver;
import com.zoe.onelink.business.service.ParamService;
import com.zoe.onelink.business.util.ExternalSystemUtil;
import com.zoe.onelink.business.util.FtpFileUtil;
import com.zoe.onelink.business.util.ParamCacheUtil;
import com.zoe.onelink.business.util.ws.WSRequestUtil;
import com.zoe.onelink.common.constant.WebConstant;
import com.zoe.onelink.core.i18n.I18nMessageProvider;
import com.zoe.onelink.enhancer.init.ApplicationInitializer;
import com.zoe.onelink.util.OnelinkContextHolder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-10-29
 */
@RequiredArgsConstructor
@EnableConfigurationProperties({OnelinkBizProperties.class, OnelinkExternalSystemProperties.class, FtpFileUtil.Prop.class})
@Configuration(proxyBeanMethods = false)
@Slf4j
@Import({FtpFileUtil.class, ExternalSystemUtil.class})
@ComponentScan({"com.zoe.onelink.business.entity", "com.zoe.onelink.business.service", "com.zoe.onelink.business.dao"})
public class BizWebAutoConfiguration implements WebMvcConfigurer, SmartInitializingSingleton {

    private final ConfigurableBeanFactory configurableBeanFactory;
    private final ObjectProvider<RequestMappingHandlerAdapter> requestMappingHandlerAdapter;

    /**
     * 如果移除该组件,需要对应移除afterPropertiesSet中的RequestMappingHandlerAdapter相关业务
     * 会导致requestMappingHandlerAdapter找不到实例
     */
    @Bean
    public FilterRegistrationBean<WrapParameterFilter> wrapParameterFilter() {
        FilterRegistrationBean<WrapParameterFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new WrapParameterFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Bean
    public ParamCacheUtil paramsCacheUtil(OnelinkBizProperties onelinkBizProperties, ParamService paramService) {
        return new ParamCacheUtil(onelinkBizProperties.getBizParam(), paramService);
    }

    @Bean
    @ConditionalOnMissingBean
    public I18nMessageProvider i18nBizCustomMessageProvider(ApplicationContext applicationContext) {
        return new I18nBizCustomMessageProvider(applicationContext);
    }

    @Bean
    public ParamService paramService(ApplicationContext applicationContext, OnelinkBizProperties onelinkBizProperties) {
        return new ParamService(applicationContext, onelinkBizProperties.getBizParam());
    }

    @Bean
    public WSRequestUtil wsRequestUtil(OnelinkBizProperties onelinkBizProperties) {
        return new WSRequestUtil(onelinkBizProperties.getBizParam());
    }

    @Bean
    public OnelinkTokenAuthenticateCallback onelinkTokenAuthenticateCallback() {
        return new OnelinkTokenAuthenticateCallback() {
            @Override
            public void onAuthenticateSuccess(Map<String, Object> authMap) {
                OnelinkContextHolder.putValue(authMap);
            }

            @Override
            public void onAuthenticateFailure(Throwable throwable) {
                // do-nothing
            }
        };
    }

    @Bean
    @ConditionalOnProperty(name = "onelink.biz.auto-init-param", matchIfMissing = false)
    public ApplicationInitializer paramInitializer(ParamDao paramDao) {
        return new BizParamInitializer(paramDao);
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new BizHeaderInterceptor()).order(200)
                .addPathPatterns("/**").excludePathPatterns(WebConstant.INSENSITIVE_URLS);

    }

    @Bean
    public Interceptor batchByJdbcInterceptor(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
        return new BatchByJdbcInterceptor(namedParameterJdbcTemplate);
    }

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public InnerInterceptor mybatisAdvanceQueryInnerInterceptor() {
        return new MybatisAdvanceQueryInnerInterceptor();
    }

    @Bean
    public OnelinkBizEndpoint onelinkBizEndpoint(ParamService paramService) {
        return new OnelinkBizEndpoint(paramService);
    }

    @Override
    public void afterSingletonsInstantiated() {
        // 以下增加的解析器专门用来解决旧项目业务组说代码改造量太大,改不动的问题
        RequestMappingHandlerAdapter adapter = requestMappingHandlerAdapter.getIfAvailable();
        if (adapter == null) {
            return;
        }
        List<HandlerMethodArgumentResolver> argumentResolvers = adapter.getArgumentResolvers();
        if (argumentResolvers == null) {
            return;
        }
        int index = CollUtil.indexOf(argumentResolvers, handlerMethodArgumentResolver ->
                handlerMethodArgumentResolver.getClass() == RequestHeaderMethodArgumentResolver.class);
        if (index == -1) {
            return;
        }
        LinkedList<HandlerMethodArgumentResolver> newArgResolvers = new LinkedList<>(argumentResolvers);
        newArgResolvers.add(index, new OnelinkRequestHeaderMethodArgumentResolver(this.configurableBeanFactory));
        adapter.setArgumentResolvers(Collections.unmodifiableList(newArgResolvers));

    }

}
